mod core;
mod utils;

use core::builder;
pub use core::config::*;
use std::path::PathBuf;
use std::process::{exit, Output};

use utils::exec::{BehaviorOnFailure, BootstrapCommand};

use crate::utils::exec::OutputMode;

/// Global configuration for the build system.
pub struct Build {
    config: Config,
    initial_rustc: PathBuf,
    initial_cargo: PathBuf,
}

impl Build {
    /// Creates a new set of build configuration from the `flags` on the command
    /// line and the filesystem `config`.
    ///
    /// By default all build output will be placed in the current directory.
    pub fn new(config: Config) -> Build {
        let build = Build {
            initial_rustc: {
                // config.download_toolchain();
                config.out.join("local/bin/rustc")
            },
            initial_cargo: {
                // config.download_toolchain();
                config.out.join("local/bin/cargo")
            },
            config,
        };

        build
    }

    /// Executes the entire build. as configured by the flags and configuration.
    pub fn build(&mut self) {
        let builder = builder::Builder::new(self);
        builder.execute_cli();
    }

    /// A centralized function for running commands that do not return output.
    pub(crate) fn run_cmd<'a, C: Into<BootstrapCommand<'a>>>(&self, cmd: C) -> bool {
        let command = cmd.into();
        println!("{}", &format!("running: {command:?}"));

        let (output, print_error) = match command.output_mode {
            mode @ (OutputMode::PrintAll | OutputMode::PrintOutput) => (
                command.command.status().map(|status| Output {
                    status,
                    stdout: Vec::new(),
                    stderr: Vec::new(),
                }),
                matches!(mode, OutputMode::PrintAll),
            ),
            OutputMode::SuppressOnSuccess => (command.command.output(), true),
        };

        let output = match output {
            Ok(output) => output,
            Err(e) => panic!("{}", &format!("failed to execute command: {:?}\nerror: {}", command, e)),
        };
        let result = if !output.status.success() {
            if print_error {
                println!(
                    "\n\ncommand did not execute successfully: {:?}\n\
                    expected success, got: {}\n\n\
                    stdout ----\n{}\n\
                    stderr ----\n{}\n\n",
                    command.command,
                    output.status,
                    String::from_utf8_lossy(&output.stdout),
                    String::from_utf8_lossy(&output.stderr)
                );
            }
            Err(())
        } else {
            Ok(())
        };

        match result {
            Ok(_) => true,
            Err(_) => {
                match command.failure_behavior {
                    BehaviorOnFailure::DelayFail => {
                        exit(1);

                    }
                    BehaviorOnFailure::Exit => {
                        exit(1);
                    }
                    BehaviorOnFailure::Ignore => {}
                }
                false
            }
        }
    }
}

#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)]
pub struct Compiler;

impl Compiler {
    
}